/*
 * TickBar.java
 *
 * Created on September 19, 2008, 11:57 PM
 */

package com.leadiv.openrule;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;

/**
 * The TickBar is a JPanel that displays tick marks every even pixel along
 * the panel. This can be configured to be displaying vertically or
 * horizontally. The default is horizontal.
 *
 * @author  leadiv
 */
public class TickBar extends javax.swing.JPanel {

    /** TickBar Creates new form TickBar */
    public TickBar() {
        super();
        this.d = new Dimension();
        this.percision = 0;
        this.labels = true;
        this.spacegap = 2;
        this.labelFont = new Font("SansSerif", Font.PLAIN, 9);
        this.policy = new TickPolicy();

        // pixel is the default tick policy
        this.policy.addLevel(2);
        this.policy.addLevel(10);
        this.policy.addLevel(20);
        this.resolution = this.policy.getUnitResolution();
        this.totalLevels = this.policy.getTotalLevels();

        this.getSize(d);
        this.endOfRuler = this.d.width;
        this.depthOfRuler = this.d.height;
        this.orientation = TickBar.HORIZONTAL;
        this.tickStick = TickBar.TICK_STICK_TOP;

        initComponents();
    }

    public TickBar(int orientation, int tickStick ) {
        super();
        this.d = new Dimension();
        this.percision = 0;
        this.labels = true;
        this.spacegap = 2;
        this.labelFont = new Font("SansSerif", Font.PLAIN, 9);
        this.policy = new TickPolicy();
        this.tickStick = TickBar.TICK_STICK_TOP;
        this.orientation = TickBar.HORIZONTAL;

        // pixel is the default tick policy
        this.policy.addLevel(2);
        this.policy.addLevel(10);
        this.policy.addLevel(20);
        this.resolution = this.policy.getUnitResolution();
        this.totalLevels = this.policy.getTotalLevels();

        this._setOrientation(orientation);
        this._setTickStick(tickStick);

        initComponents();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        if( TickBar.VERTICAL == this.orientation ) {
            this.verticalDraw(g);
            return;
        }

        this.horizontalDraw(g);
    }

    /**
     * Draws the ticks and labels when the TickBar is in horizontal mode.
     * @param g The graphics context to draw on.
     */
    private void horizontalDraw(Graphics g) {
        int level, tickHeight;
        double unit;
        TextLayout layout;
        Rectangle2D txtbounds;
        
        int i = 0;
        Graphics2D g2 = (Graphics2D)g;
        FontRenderContext frc = g2.getFontRenderContext();
        String labelFrmt = "%." + this.percision + "f";

        // special case. There is nothing to draw!
        if(0 == this.totalLevels) return;

        this.setOrientation(TickBar.HORIZONTAL);
        for(;i < endOfRuler; i++)
        {
            level = this.policy.getPxLevel(i);
            tickHeight = (TickBar.MAX_TICK_HEIGHT * level) / this.totalLevels;
            if(this.labels && level == this.totalLevels) {
                unit = (double)i / this.resolution;
                layout = new TextLayout(String.format(labelFrmt, unit), this.labelFont, frc);
                txtbounds = layout.getBounds();

                if( TickBar.TICK_STICK_BOTTOM == this.tickStick ) {
                    layout.draw( g2,
                        (float)(i == 0 ? i : i - txtbounds.getWidth() / 2),
                        depthOfRuler - tickHeight - 2 );
                } else {
                    layout.draw( g2,
                        (float)(i == 0 ? i : i - txtbounds.getWidth() / 2),
                        (float)(tickHeight + txtbounds.getHeight()) + 2 );
                }
            }

            if( TickBar.TICK_STICK_BOTTOM == this.tickStick ) {
                g.drawLine(i, depthOfRuler, i, depthOfRuler - tickHeight);
            } else {
                g.drawLine(i, 0, i, tickHeight);
            }
        }
    }

    /**
     * Draws the ticks and labels when the TickBar is in vertical mode.
     * @param g The graphics context to draw on.
     */
    private void verticalDraw(Graphics g) {
        int level, tickWidth;
        double unit;
        TextLayout layout;
        Rectangle2D txtbounds;

        int i = 0;
        Graphics2D g2 = (Graphics2D)g;
        FontRenderContext frc = g2.getFontRenderContext();
        String labelFrmt = "%." + this.percision + "f";
        

        this.setOrientation(TickBar.VERTICAL);
        for(;i < endOfRuler; i++)
        {
            level = this.policy.getPxLevel(i);
            tickWidth = (TickBar.MAX_TICK_HEIGHT * level) / this.totalLevels;

            if(this.labels && level == this.totalLevels) {
                unit = (double)i / this.resolution;
                layout = new TextLayout(String.format(labelFrmt, unit), this.labelFont, frc);
                txtbounds = layout.getBounds();

                if( TickBar.TICK_STICK_BOTTOM == this.tickStick ) {
                    layout.draw(g2,
                        (float)(depthOfRuler - tickWidth - txtbounds.getWidth() - this.spacegap),
                        (i != 0) ? i + ((float)txtbounds.getHeight() / 2) : (float)txtbounds.getHeight());
                } else {
                    layout.draw(g2,
                        (float)(tickWidth + this.spacegap),
                        (i != 0) ? i + ((float)txtbounds.getHeight() / 2) : (float)txtbounds.getHeight());
                }
            }

            if( TickBar.TICK_STICK_BOTTOM == this.tickStick ) {
                g.drawLine(depthOfRuler, i, depthOfRuler - tickWidth, i);
            } else {
                g.drawLine(0, i, tickWidth, i);
            }
        }
    }
    
    public void toggle() {
        int o = (TickBar.VERTICAL == this.orientation) ? TickBar.HORIZONTAL : TickBar.VERTICAL;
        this.setOrientation(o);
    }

    public TickPolicy getPolicy() {
        return policy;
    }

    public void setPolicy(TickPolicy policy) {
        this.policy = policy;
        this.resolution = this.policy.getUnitResolution();
        this.totalLevels = this.policy.getTotalLevels();
        
    }

    public boolean isLabels() {
        return labels;
    }

    public void setLabels(boolean labels) {
        this.labels = labels;
    }

    public int getPercision() {
        return percision;
    }

    public void setPercision(int percision) {
        this.percision = percision;
    }

    public Font getLabelFont() {
        return labelFont;
    }

    public void setLabelFont(Font labelFont) {
        this.labelFont = labelFont;
    }

    public int getSpacegap() {
        return spacegap;
    }

    public void setSpacegap(int spacegap) {
        this.spacegap = spacegap;
    }

    public int isOrientation() {
        return orientation;
    }

    public void setOrientation(int orientation) {
        this._setOrientation(orientation);
    }

    private void _setOrientation(int orientation){
        if( TickBar.VERTICAL == this.orientation ) {
            this.getSize(d);
            this.endOfRuler = this.d.height;
            this.depthOfRuler = this.d.width;
            this.orientation = orientation;
        }

        if( TickBar.HORIZONTAL == this.orientation ) {
            this.getSize(d);
            this.endOfRuler = this.d.width;
            this.depthOfRuler = this.d.height;
            this.orientation = orientation;
        }
    }

    public int getTickStick() {
        return tickStick;
    }

    public void setTickStick(int tickStick) {
        this._setOrientation(tickStick);
    }

    private void _setTickStick(int tickStick) {
        if( TickBar.TICK_STICK_TOP == tickStick || TickBar.TICK_STICK_BOTTOM == tickStick ) {
            this.tickStick = tickStick;
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setLayout(null);
    }// </editor-fold>//GEN-END:initComponents

   private Dimension d;                               // This should not be touched by users.
   private int endOfRuler, depthOfRuler, totalLevels; // This should not be touched by users.
   private float resolution;                          // This should not be touched by users.
   private int spacegap, percision, orientation, tickStick;
   private boolean labels;
   private Font labelFont;
   private TickPolicy policy;
   public static int MAX_TICK_HEIGHT = 20;
   public static int VERTICAL = 0;
   public static int HORIZONTAL = 1;
   public static int TICK_STICK_TOP = 2;
   public static int TICK_STICK_BOTTOM = 3;

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

}
